/*
****************************************************************************************
** braif: LCD和GUI层
**
** Author: xiongjinqi
** Date:  2022/11/30
** History: 
        2022/11/30: 初始版本
****************************************************************************************
*/
#define LOG_TAG          "DEV_LCD"
#include <elog.h>

#include "dev_lcd.h"
#include "dev_ili9341.h"
#include <stdlib.h>
#include <string.h>
/*
    LCD 参数定义
*/
_lcd_param LCD_ILI9341 = {
    .id = 0x9341,
    .width = 240,
    .height = 320,
};


_lcd_param *LCD_Devicelist[] = 
{
    &LCD_ILI9341,
};

/*
    LCD 驱动列表
*/
_lcd_drv *LCD_Drvlist[] = 
{
    &TFTLCD_ILI9341Drv,
};


/*
    设备树定义
    指明系统有多少个LCD，挂载哪个设备总线上
*/
#define DEV_LCD_C 4//系统存在3个LCD设备
_lcd_obj LcdObjList[]=
{
    {"oledlcd", LCD_BUS_VSPI, 0X1315},
    {"coglcd", LCD_BUS_SPI,  0X7565},
    {"tftlcd", LCD_BUS_8080, NULL},
};

/*LCD设备总结构体，初始化设备时初始化*/
DevLcd DevLcdList[DEV_LCD_C];


/**
 *@brief:      dev_lcd_cpydev
 *@details:    拷贝设备信息
 *@param[in]   DevLcd *src  
               DevLcd *dst  
 *@param[out]  无
 *@retval:     
 */
static s32 dev_lcd_cpydev(DevLcd *src, DevLcd *dst)
{
    src->gd = dst->gd;
    src->dev = dst->dev;
    src->param = dst->param;
    src->drv = dst->drv;
    src->dir = dst->dir;
    src->scandir = dst->scandir;
    src->width = dst->width;
    src->height = dst->height;
    return 0;
}

/**
 *@brief:      dev_lcd_findpra
 *@details:    根据ID查找LCD参数
 *@param[in]   u16 id  
 *@param[out]  无
 *@retval:     _lcd_pra
 */
static _lcd_param *dev_lcd_findpra(u16 id)
{
    u8 i =0;
    
    while(1)
    {
        if(LCD_Devicelist[i]->id == id)
        {
            return LCD_Devicelist[i];
        }
        i++;
        if(i>= sizeof(LCD_Devicelist)/sizeof(_lcd_param *))
        {
            return NULL;
        }
    }
    
}
/**
 *@brief:      dev_lcd_finddrv
 *@details:    根据ID查找设备驱动
 *@param[in]   u16 id  
 *@param[out]  无
 *@retval:     _lcd_drv
 */
_lcd_drv *dev_lcd_finddrv(u16 id)
{
    u8 i =0;
    
    while(1)
    {
        if(LCD_Drvlist[i]->id == id)
        {
            return LCD_Drvlist[i];
        }
        i++;
        if(i>= sizeof(LCD_Drvlist)/sizeof(_lcd_drv *))
        {
            return NULL;
        }
    }
}

/**
 *@brief:      dev_lcd_setdir
 *@details:    设置横屏或竖屏，扫描方向
 *@param[in]   u8 dir       0 竖屏1横屏
               u8 scan_dir  参考宏定义L2R_U2D       
 *@param[out]  无
 *@retval:     
 */
s32 dev_lcd_setdir(DevLcd *lcd, u8 dir, u8 scan_dir)
{
    u16 temp;
    u8 scan_dir_tmp;

    if(lcd == NULL)
        return -1;

    
    if(dir != lcd->dir)//切换屏幕方向 
    {
        
        lcd->dir = lcd->dir^0x01;
        temp = lcd->width;
        lcd->width = lcd->height;
        lcd->height = temp;
        log_d( "set dir w:%d, h:%d\r\n", lcd->width, lcd->height);
    }
    
    
    if(lcd->dir == W_LCD)//横屏，扫描方向映射转换
    {
        /*
            横屏   竖屏
            LR----UD
            RL----DU
            UD----RL
            DU----LR
            UDLR----LRUD
        */
        scan_dir_tmp = 0;
        if((scan_dir&LRUD_BIT_MASK) == 0)
        {
            scan_dir_tmp += LRUD_BIT_MASK;
        }

        if((scan_dir&LR_BIT_MASK) == LR_BIT_MASK)
        {
            scan_dir_tmp += UD_BIT_MASK;    
        }

        if((scan_dir&UD_BIT_MASK) == 0)
        {
            scan_dir_tmp += LR_BIT_MASK;
        }
    }
    else
    {
        scan_dir_tmp = scan_dir;
    }
    
    lcd->scandir = scan_dir_tmp;
    
    lcd->drv->set_dir(lcd, lcd->scandir);
    
    return 0;
}



/**
 *@brief:      dev_lcd_init
 *@details:    初始化LCD
               根据设备树，初始化所有LCD
 *@param[in]   void  
 *@param[out]  无
 *@retval:     
 */
s32 dev_lcd_init(void)
{
    s32 ret = -1;
    u8 i = 0;
    _lcd_obj *pobj;
    DevLcd *pdev;
    
    while(1)
    {   
        pobj = &LcdObjList[i];
        pdev = &DevLcdList[i];
        
        log_i( "\r\nlcd name:%s\r\n",pobj->name);

        pdev->dev = pobj;
        pdev->gd = -99;//初始化成功就设置为-1；
        ret = -1;
        /*
            根据ID找驱动跟参数
        */
        if(pobj->id == NULL)
        {
        #if 0
            log_d( "prob LCD id\r\n");
            if(pobj->bus == LCD_BUS_8080)
            {
                /*找到驱动跟规格后，初始化*/
                u8 j = 0;

                while(1)
                {
                    ret = LcdProbDrv8080List[j]->init(pdev);
                    if(ret == 0)
                    {
                        log_d( "lcd drv prob ok!\r\n");   
                        pdev->drv = LcdProbDrv8080List[j];
                        /*
                            用驱动的ID找参数
                        */
                        pdev->pra = dev_lcd_findpra(pdev->drv->id);
                        break;
                    }   
                    else
                    {
                        j++;
                        if(j >= sizeof(LcdProbDrv8080List)/sizeof(_lcd_drv *))
                        {
                            log_d( "lcd prob err\r\n");
                            break;
                        }
                    }
                }
            }
        #endif /* 0 */
        }
        else
        {
            pdev->drv = dev_lcd_finddrv(pobj->id);
            if(pdev->drv != NULL)
            {
                pdev->param = dev_lcd_findpra(pobj->id);
                if(pdev->param != NULL)
                {
                    /*找到驱动跟规格后，初始化*/
                    ret = pdev->drv->init(pdev);
                }
                else
                    log_d( "lcd find drv fail!\r\n");
            }
            else
                log_d( "lcd find drv fail!\r\n");
        }

        if(ret == 0)
        {
            pdev->gd = -1;
            
            pdev->dir = H_LCD;
            pdev->height = pdev->param->height;
            pdev->width = pdev->param->width;
            dev_lcd_setdir(pdev, W_LCD, L2R_U2D);
            pdev->drv->onoff((pdev),1);//打开显示
            pdev->drv->color_fill(pdev, 0, pdev->width, 0, pdev->height, BLUE);

            pdev->drv->backlight(pdev, 1);

            log_i( "lcd init OK\r\n");
        }
        else
        {
            log_i( "lcd drv init err!\r\n");
        }
        

        i++;
        if(i >= sizeof(LcdObjList)/sizeof(_lcd_obj))
        {
            log_i( "lcd init finish\r\n");
            break;
        }
    }

    return 0;
}
/**
 *@brief:      dev_lcd_open
 *@details:    打开LCD
 *@param[in]   char *name  
 *@param[out]  无
 *@retval:     DevLcd
 */
DevLcd *dev_lcd_open(char *name)
{
    DevLcd *p;
    u8 i;

    i = 0;
    while(1)
    {
        p = &DevLcdList[i];
        if(0 == strcmp(name, p->dev->name))
        {
            log_d( "find lcd\r\n");
            
            if(p->gd == -99)
            {
                log_d( "lcd dev no init!\r\n");
                return NULL;
            }
            return p;
        }
        
        i++;
        if(i>= sizeof(LcdObjList)/sizeof(_lcd_obj))
            return NULL;
    }
}
/**
 *@brief:      dev_lcd_close
 *@details:    关闭LCD
 *@param[in]   DevLcd *dev  
 *@param[out]  无
 *@retval:     
 */
s32 dev_lcd_close(DevLcd *dev)
{
    if(dev->gd <0)
        return -1;
    else
    {
        dev->gd = -1;
        return 0;
    }
}
/*
坐标-1 是坐标原点的变化，
在APP层，原点是（1，1），这样更符合平常人。

到驱动就换为(0,0)，无论程序还是控制器显存，都是从（0，0）开始

*/
s32 dev_lcd_drawpoint(DevLcd *lcd, u16 x, u16 y, u16 color)
{
    if(lcd == NULL)
        return -1;

    return lcd->drv->draw_point(lcd, x-1, y-1, color);
}

s32 dev_lcd_prepare_display(DevLcd *lcd, u16 sx, u16 ex, u16 sy, u16 ey)
{
    if(lcd == NULL)
        return -1;
    
    return lcd->drv->prepare_display(lcd, sx-1, ex-1, sy-1, ey-1);
}


s32 dev_lcd_fill(DevLcd *lcd, u16 sx,u16 ex,u16 sy,u16 ey,u16 *color)
{   
    if(lcd == NULL)
        return -1;
    
    return lcd->drv->fill(lcd, sx-1,ex-1,sy-1,ey-1,color);
}
s32 dev_lcd_color_fill(DevLcd *lcd, u16 sx,u16 ex,u16 sy,u16 ey,u16 color)
{
    if(lcd == NULL)
        return -1;
    
    return lcd->drv->color_fill(lcd, sx-1,ex-1,sy-1,ey-1,color);
}
s32 dev_lcd_backlight(DevLcd *lcd, u8 sta)
{
    if(lcd == NULL)
        return -1;
    
    lcd->drv->backlight(lcd, sta);
    return 0;
}

s32 dev_lcd_display_onoff(DevLcd *lcd, u8 sta)
{
    if(lcd == NULL)
        return -1;

    return lcd->drv->onoff(lcd, sta);
}
/* 

从tslib拷贝一些显示函数到这里
这些函数可以归为GUI
*/
#include "font.h"

/**
 *@brief:      line
 *@details:    画一条线
 *@param[in]   int x1           
               int y1           
               int x2           
               int y2           
               unsigned colidx  
 *@param[out]  无
 *@retval:     
 */
void line (DevLcd *lcd, int x1, int y1, int x2, int y2, unsigned colidx)
{
    int tmp;
    int dx = x2 - x1;
    int dy = y2 - y1;

    if(lcd == NULL)
        return;

    if (abs (dx) < abs (dy)) 
    {
        if (y1 > y2) 
        {
            tmp = x1; x1 = x2; x2 = tmp;
            tmp = y1; y1 = y2; y2 = tmp;
            dx = -dx; dy = -dy;
        }
        x1 <<= 16;
        /* dy is apriori >0 */
        dx = (dx << 16) / dy;
        while (y1 <= y2)
        {
            dev_lcd_drawpoint(lcd, x1 >> 16, y1, colidx);
            x1 += dx;
            y1++;
        }
    } 
    else 
    {
        if (x1 > x2) 
        {
            tmp = x1; x1 = x2; x2 = tmp;
            tmp = y1; y1 = y2; y2 = tmp;
            dx = -dx; dy = -dy;
        }
        
        y1 <<= 16;
        dy = dx ? (dy << 16) / dx : 0;
        while (x1 <= x2) 
        {
            dev_lcd_drawpoint(lcd, x1, y1 >> 16, colidx);
            y1 += dy;
            x1++;
        }
    }
}

/**
 *@brief:     put_cross
 *@details:   画十字
 *@param[in]  int x            
              int y            
              unsigned colidx  
 *@param[out]  无
 *@retval:     
 */
void put_cross(DevLcd *lcd, int x, int y, unsigned colidx)
{
    if(lcd == NULL)
        return;
    
    line (lcd, x - 10, y, x - 2, y, colidx);
    line (lcd, x + 2, y, x + 10, y, colidx);
    line (lcd, x, y - 10, x, y - 2, colidx);
    line (lcd, x, y + 2, x, y + 10, colidx);

    line (lcd, x - 6, y - 9, x - 9, y - 9, colidx + 1);
    line (lcd, x - 9, y - 8, x - 9, y - 6, colidx + 1);
    line (lcd, x - 9, y + 6, x - 9, y + 9, colidx + 1);
    line (lcd, x - 8, y + 9, x - 6, y + 9, colidx + 1);
    line (lcd, x + 6, y + 9, x + 9, y + 9, colidx + 1);
    line (lcd, x + 9, y + 8, x + 9, y + 6, colidx + 1);
    line (lcd, x + 9, y - 6, x + 9, y - 9, colidx + 1);
    line (lcd, x + 8, y - 9, x + 6, y - 9, colidx + 1);

}
/**
 *@brief:      put_char
 *@details:    显示一个英文
 *@param[in]   int x       
               int y       
               int c       
               int colidx  
 *@param[out]  无
 *@retval:     
 */
void put_char(DevLcd *lcd, int x, int y, int c, int colidx)
{
    int i,j,bits;
    u8* p;
    
    if(lcd == NULL)
        return; 
    
    p = (u8*)font_vga_8x16.data;
    for (i = 0; i < font_vga_8x16.height; i++) 
    {
        bits =  p[font_vga_8x16.height * c + i];
        for (j = 0; j < font_vga_8x16.width; j++, bits <<= 1)
        {
            if (bits & 0x80)
            {
                lcd->drv->draw_point(lcd, x + j, y + i, colidx);
            }
        }
    }
}
/**
 *@brief:      put_string
 *@details:    显示一个字符串
 *@param[in]   int x            
               int y            
               char *s          
               unsigned colidx  
 *@param[out]  无
 *@retval:     
 */
void put_string(DevLcd *lcd, int x, int y, char *s, unsigned colidx)
{
    int i;
    
    if(lcd == NULL)
        return; 
    
    for (i = 0; *s; i++, x += font_vga_8x16.width, s++)
        put_char(lcd, x, y, *s, colidx);
}
/**
 *@brief:      put_string_center
 *@details:    居中显示一个字符串
 *@param[in]   int x            
               int y            
               char *s          
               unsigned colidx  
 *@param[out]  无
 *@retval:     
 */
void put_string_center(DevLcd *lcd, int x, int y, char *s, unsigned colidx)
{
    int sl = strlen (s);
    
    if(lcd == NULL)
        return; 
    
    put_string (lcd, x - (sl / 2) * font_vga_8x16.width,
                y - font_vga_8x16.height / 2, s, colidx);
}

/**
 *@brief:      rect
 *@details:    画一个矩形框
 *@param[in]   int x1           
               int y1           
               int x2           
               int y2           
               unsigned colidx  
 *@param[out]  无
 *@retval:     
 */
void rect (DevLcd *lcd, int x1, int y1, int x2, int y2, unsigned colidx)
{
    if(lcd == NULL)
        return;

    line (lcd, x1, y1, x2, y1, colidx);
    line (lcd, x2, y1, x2, y2, colidx);
    line (lcd, x2, y2, x1, y2, colidx);
    line (lcd, x1, y2, x1, y1, colidx);
}


/**
 *@brief:      dev_lcd_test
 *@details:    LCD测试函数
 *@param[in]   void  
 *@param[out]  无
 *@retval:     
 */
void dev_lcd_test(void)
{
    DevLcd *LcdCog = NULL;
    DevLcd *LcdOled = NULL;
    DevLcd *LcdOledI2C = NULL;
    DevLcd *LcdTft = NULL;

    /*  打开三个设备 */
    LcdCog = dev_lcd_open("spicoglcd");
    if(LcdCog==NULL)
        log_d( "open cog lcd err\r\n");

    LcdOled = dev_lcd_open("vspioledlcd");
    if(LcdOled==NULL)
        log_d( "open oled lcd err\r\n");
    
    LcdTft = dev_lcd_open("tftlcd");
    if(LcdTft==NULL)
        log_d( "open tft lcd err\r\n");

    LcdOledI2C = dev_lcd_open("i2coledlcd");
    if(LcdOledI2C==NULL)
        log_d( "open oled i2c lcd err\r\n");
    
    /*打开背光*/
    dev_lcd_backlight(LcdCog, 1);
    dev_lcd_backlight(LcdOled, 1);
    dev_lcd_backlight(LcdOledI2C, 1);
    dev_lcd_backlight(LcdTft, 1);

    put_string(LcdCog, 5, 5, "spi cog lcd", BLACK);
    put_string(LcdOled, 5, 5, "vspi oled lcd", BLACK);
    put_string(LcdOledI2C, 5, 5, "i2c oled lcd", BLACK);
    put_string(LcdTft, 5, 5, "2.8 tft lcd", BLACK);

    while(1);
}


